1 /*
2 * Scope: a generic MVC framework.
3 * Copyright (c) 2000-2002, The Scope team
4 * All rights reserved.
5 *
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * Neither the name "Scope" nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 *
36 * $Id: SComboBox.java,v 1.13 2002/09/13 17:04:39 ludovicc Exp $
37 */
38 package org.scopemvc.view.swing;
39
40
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.beans.Beans;
44 import javax.swing.ComboBoxEditor;
45 import javax.swing.ComboBoxModel;
46 import javax.swing.JComboBox;
47 import javax.swing.JToolTip;
48 import javax.swing.ListCellRenderer;
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51 import org.scopemvc.core.Control;
52 import org.scopemvc.core.Controller;
53 import org.scopemvc.core.Selector;
54 import org.scopemvc.core.View;
55 import org.scopemvc.util.convertor.StringConvertor;
56
57 /***
58 * <P>
59 *
60 * A JComboBox that uses a {@link SComboBoxModel} to bind to model properties
61 * for the list of items and to bind the selected item to a property. Uses a
62 * {@link SListCellRenderer} to draw items in the list. Uses a {@link
63 * SComboBoxEditor} if editable. </P> <P>
64 *
65 * SComboBox can issue Controls on selection changes. </P> <P>
66 *
67 * SList doesn't itself bind to a model: it delegates all binding to its
68 * SComboBoxModel. </P> <P>
69 *
70 * Examples for SComboBox use can be found in <code>samples.swing.combodemo</code>
71 * package, and see {@link SComboBoxModel} and {@link SAbstractListModel}. </P>
72 * <p>
73 *
74 * Elements (rows) in the data model can be arbitrary class (e.g. Person,
75 * Employee etc.). Scope's combobox renderer can have set Selector for desired
76 * element attribute (e.g. "name"). If selector is ommited, then toString()
77 * value is drawn. If elements are of types for which exist StringConvertor, the
78 * combo box can be editable (exactly not the elements, but selected item
79 * property, however it has make sense to have elements and selected item of the
80 * same type.) </p> <p>
81 *
82 * <i>Data model</i> will be typically array (<code>Object[]</code>) or list
83 * derived from <code>java.util.List</code>. For this case there is no need to
84 * set size model. For example: <pre>
85 * class SelectedPerson {
86 * ...
87 * public void setSelectedPerson(Person p) {...}
88 * public Person getSelectedPerson() {...}
89 * }
90 * ...
91 * List persons = getSomePersons();
92 * combo.setDataModel(persons);
93 * combo.setSelectionSelectorString("selectedPerson"); // a property of the bound view model
94 * </pre> </p> <p>
95 *
96 * Some times data elements of combobox changes during combobox life cycle. For
97 * this case is better to use single model for both selection and element list.
98 * The list is treated as model property and can be changed: <pre>
99 * class PersonSelection extends BasicModel {
100 * public final static Selector PERSONS = Selector.fromString("persons");
101 * ...
102 * public void setSelectedPerson(Person p) {...}
103 * public Person getSelectedPerson() {...}
104 * public List getPersons() {...}
105 *
106 * ...
107 * // in some method
108 * persons = fetchOtherPersons();
109 * super.fireModelChange(ModelChangeEvent.VALUE_CHANGED, PERSONS);
110 * // now are data in combobox refreshed
111 * ...
112 * }
113 * ...
114 * SComboBox combo = new SComboBox();
115 * combo.setSelector(PERSONS);
116 * combo.setSelectionSelectorString("selectedPerson");
117 * ...
118 * </pre> Note that since <i>persons</i> attribute is of type <code>java.util.List</code>
119 * , there is again no need to set size selector. </p> <p>
120 *
121 * In the last variation of the example is shown, where the size model and
122 * selector is need. Java Beans specifiaction says, that indexed properties can
123 * be exposed as a single array value. However, this is not requirement. Let's
124 * see example: <pre>
125 * class Persons {
126 * ...
127 * public Person getPerson(int i) {...}
128 * public void setPerson(int i, Person p) {...}
129 *
130 * public int getPersonsNumber() {...}
131 * }
132 * ...
133 * SComboBox combo = new SComboBox();
134 * combo.setSelectorString("person");
135 * combo.setSizeSelectorString("personsNumber");
136 * ...
137 * </pre> </p>
138 *
139 * @author <A HREF="mailto:daniel.michalik@autel.cz">Daniel Michalik</A>
140 * @author <A HREF="mailto:smeyfroi@users.sourceforge.net">Steve Meyfroidt</A>
141 * @created 05 September 2002
142 * @version $Revision: 1.13 $ $Date: 2002/09/13 17:04:39 $
143 * @see SComboBoxModel
144 * @see SAbstractListModel
145 * @see SList
146 */
147 public class SComboBox extends JComboBox
148 implements View, Refreshable {
149
150 private static final Log LOG = LogFactory.getLog(SComboBox.class);
151
152 // -------------------- Controls -----------------------
153
154 private String selectionControlID;
155
156 // ------------- Support validation failures from selection -------------
157
158 /***
159 * Helper to manage validation state.
160 */
161 private ValidationHelper validationHelper = new ValidationHelper(this);
162
163
164 /***
165 * Constructor for the SComboBox object
166 */
167 public SComboBox() {
168 setRenderer(new SListCellRenderer());
169 setEditor(new SComboBoxEditor());
170 setModel(new SComboBoxModel(this));
171 addActionListener(
172 new ActionListener() {
173 public void actionPerformed(ActionEvent inEvent) {
174 issueChangeSelectionControl();
175 }
176 });
177 setEnabled(Beans.isDesignTime());
178 updateUI();
179 }
180
181
182 // ---------- Implement View by delegation to SListModel and SListSelectionModel ----------
183
184 /***
185 * Gets the bound model
186 *
187 * @return The boundModel value
188 */
189 public final Object getBoundModel() {
190 return ((SComboBoxModel) getModel()).getBoundModel();
191 }
192
193
194 /***
195 * Gets the selector
196 *
197 * @return The selector value
198 */
199 public final Selector getSelector() {
200 return ((SComboBoxModel) getModel()).getSelector();
201 }
202
203
204 /***
205 * Gets the selection selector
206 *
207 * @return The selectionSelector value
208 */
209 public final Selector getSelectionSelector() {
210 return ((SComboBoxModel) getModel()).getSelectionSelector();
211 }
212
213
214 /***
215 * Gets the renderer selector
216 *
217 * @return The rendererSelector value
218 */
219 public final Selector getRendererSelector() {
220 ListCellRenderer renderer = getRenderer();
221 if (!(renderer instanceof SListCellRenderer)) {
222 return null;
223 }
224 return ((SListCellRenderer) renderer).getTextSelector();
225 }
226
227
228 /***
229 * Gets the renderer icon selector
230 *
231 * @return The rendererIconSelector value
232 */
233 public final Selector getRendererIconSelector() {
234 ListCellRenderer renderer = getRenderer();
235 if (!(renderer instanceof SListCellRenderer)) {
236 return null;
237 }
238 return ((SListCellRenderer) renderer).getIconSelector();
239 }
240
241
242 /***
243 * Gets the size selector
244 *
245 * @return The sizeSelector value
246 */
247 public final Selector getSizeSelector() {
248 return ((SComboBoxModel) getModel()).getSizeSelector();
249 }
250
251 /***
252 * Gets the change selection control ID
253 *
254 * @return The changeSelectionControlID value
255 */
256 public final String getChangeSelectionControlID() {
257 return selectionControlID;
258 }
259
260
261 /***
262 * TODO: document the method
263 */
264 public void issueChangeSelectionControl() {
265 if (selectionControlID != null) {
266 Control control = new Control(selectionControlID);
267 issueControl(control);
268 }
269 }
270
271
272 /***
273 * Don't assign a direct Controller to ComboBox, instead delegate to the
274 * containing SwingView that has a parent Controller.
275 *
276 * @return The controller value
277 */
278 public Controller getController() {
279 return null;
280 }
281
282
283 /***
284 * Don't assign a direct Controller to ComboBox, instead delegate to the
285 * containing SwingView that has a parent Controller.
286 *
287 * @param inControl TODO: Describe the Parameter
288 */
289 public void issueControl(Control inControl) {
290 SwingUtil.issueControl(this, inControl);
291 }
292
293
294 /***
295 * Sets the bound model
296 *
297 * @param inModel The new boundModel value
298 */
299 public final void setBoundModel(Object inModel) {
300 ((SComboBoxModel) getModel()).setBoundModel(inModel);
301 }
302
303 /***
304 * Set the Selector for the list data. Should be a java.util.List or an
305 * Object[] or have a "size" property and properties accessible by an
306 * IntIndexedSelector.
307 *
308 * @param inSelector The new selector value
309 */
310 public final void setSelector(Selector inSelector) {
311 ((SComboBoxModel) getModel()).setSelector(inSelector);
312 }
313
314
315 /***
316 * Set the Selector for the list data. Should be a java.util.List or an
317 * Object[] or have a "size" property and properties accessible by an
318 * IntIndexedSelector.
319 *
320 * @param inSelectorString The new selectorString value
321 */
322 public final void setSelectorString(String inSelectorString) {
323 ((SComboBoxModel) getModel()).setSelectorString(inSelectorString);
324 }
325
326 /***
327 * Set the Selector for the list selection: this property will be bound to
328 * the list's single selection.
329 *
330 * @param inSelector The new selectionSelector value
331 */
332 public final void setSelectionSelector(Selector inSelector) {
333 ((SComboBoxModel) getModel()).setSelectionSelector(inSelector);
334 }
335
336
337 /***
338 * Set the Selector for the list selection: this property will be bound to
339 * the list's single selection.
340 *
341 * @param inSelectorString The new selectionSelectorString value
342 */
343 public final void setSelectionSelectorString(String inSelectorString) {
344 ((SComboBoxModel) getModel()).setSelectionSelectorString(inSelectorString);
345 }
346
347 /***
348 * Set the Selector for the list cell renderer: this is the property that
349 * will be shown in a list cell (converted to a String).
350 *
351 * @param inSelector The new rendererSelector value
352 */
353 public final void setRendererSelector(Selector inSelector) {
354 ListCellRenderer renderer = getRenderer();
355 if (!(renderer instanceof SListCellRenderer)) {
356 throw new UnsupportedOperationException("Not using a Scope bound SListCellRenderer so can't set a Selector on it");
357 }
358 ((SListCellRenderer) renderer).setTextSelector(inSelector);
359 }
360
361 /***
362 * Set the Selector for the list cell renderer to get an Icon: this is the
363 * property that will be shown as an Icon in a list cell.
364 *
365 * @param inSelector The new rendererIconSelector value
366 */
367 public final void setRendererIconSelector(Selector inSelector) {
368 ListCellRenderer renderer = getRenderer();
369 if (!(renderer instanceof SListCellRenderer)) {
370 throw new UnsupportedOperationException("Not using a Scope bound SListCellRenderer so can't set a Selector on it");
371 }
372 ((SListCellRenderer) renderer).setIconSelector(inSelector);
373 }
374
375
376 /***
377 * Set the Selector for the list cell renderer: this is the property that
378 * will be shown in a list cell (converted to a String).
379 *
380 * @param inSelectorString The new rendererSelectorString value
381 */
382 public final void setRendererSelectorString(String inSelectorString) {
383 ListCellRenderer renderer = getRenderer();
384 if (!(renderer instanceof SListCellRenderer)) {
385 throw new UnsupportedOperationException("Not using a Scope bound SListCellRenderer so can't set a Selector on it");
386 }
387 ((SListCellRenderer) renderer).setTextSelectorString(inSelectorString);
388 }
389
390
391 /***
392 * Set the Selector for the list cell renderer to get an Icon: this is the
393 * property that will be shown as an Icon in a list cell.
394 *
395 * @param inSelectorString The new rendererIconSelectorString value
396 */
397 public final void setRendererIconSelectorString(String inSelectorString) {
398 ListCellRenderer renderer = getRenderer();
399 if (!(renderer instanceof SListCellRenderer)) {
400 throw new UnsupportedOperationException("Not using a Scope bound SListCellRenderer so can't set a Selector on it");
401 }
402 ((SListCellRenderer) renderer).setIconSelectorString(inSelectorString);
403 }
404
405 /***
406 * Optional: set the Selector for the property that is the size of the items
407 * list. Not needed for lists that are of type Object[] or java.util.List.
408 *
409 * @param inSelector The new sizeSelector value
410 */
411 public final void setSizeSelector(Selector inSelector) {
412 ((SComboBoxModel) getModel()).setSizeSelector(inSelector);
413 }
414
415
416 /***
417 * Optional: set the Selector for the property that is the size of the items
418 * list. Not needed for lists that are of type Object[] or java.util.List.
419 *
420 * @param inSelectorString The new sizeSelectorString value
421 */
422 public final void setSizeSelectorString(String inSelectorString) {
423 ((SComboBoxModel) getModel()).setSizeSelectorString(inSelectorString);
424 }
425
426 /***
427 * Set the Control ID for the Control that will be issued when the selection
428 * is changed. If null no Control will be issued.
429 *
430 * @param inControlID The new changeSelectionControlID value
431 */
432 public final void setChangeSelectionControlID(String inControlID) {
433 selectionControlID = inControlID;
434 }
435
436
437 /***
438 * Force use of this StringConvertor instead of automatically finding one to
439 * match the datatype being edited.
440 *
441 * @param inConvertor The new stringConvertor value
442 */
443 public void setStringConvertor(StringConvertor inConvertor) {
444 ComboBoxEditor editor = (SComboBoxEditor) getEditor();
445 if (editor instanceof SComboBoxEditor) {
446 ((SComboBoxEditor) editor).setStringConvertor(inConvertor);
447 }
448 }
449
450
451 /***
452 * This method is overriden to fix JDK 1.2.x,1.3.x bug: after setting model
453 * combobox sets element 0 as selected. This is violation of MVC paradigm -
454 * model describes some state and should not be modified by view. JDK 1.4
455 * beta has this bug fixed. <p>
456 *
457 * Model should be instance of SComboBoxModel to fix take effect</p> .
458 *
459 * @param m The new model value
460 * @see SComboBoxModel#setSelectedItem(Object)
461 */
462 public void setModel(ComboBoxModel m) {
463 if (m instanceof SComboBoxModel) {
464 ((SComboBoxModel) m).jdk1_3fixIgnoreSetSelectedItem(true);
465 super.setModel(m);
466 ((SComboBoxModel) m).jdk1_3fixIgnoreSetSelectedItem(false);
467 } else {
468 super.setModel(m);
469 }
470 }
471
472
473 // --------- Set up list binding by delegation -------------
474
475 /***
476 * Can use this to specify a static list model for the contents of the list
477 * rather than binding to a dynamic property of some view model.
478 *
479 * @param inModel The new listModel value
480 * @see org.scopemvc.model.collection.ListModelAdaptor
481 */
482 public void setListModel(Object inModel) {
483 ((SComboBoxModel) getModel()).setListModel(inModel);
484 }
485
486 /***
487 * Don't assign a Controller to this component, instead delegate to the
488 * containing SwingView that has a parent Controller.
489 *
490 * @param inController The new controller value
491 */
492 public void setController(Controller inController) {
493 throw new UnsupportedOperationException("Can't assign a Controller to a " + getClass());
494 }
495
496 // ------------------ Refreshable -------------------------
497
498 /***
499 * TODO: document the method
500 */
501 public void refresh() {
502 ((SComboBoxModel) getModel()).refresh();
503 }
504
505
506 /***
507 * TODO: document the method
508 *
509 * @param inException TODO: Describe the Parameter
510 */
511 public void validationFailed(Exception inException) {
512 validationHelper.validationFailed(inException);
513 }
514
515
516 /***
517 * TODO: document the method
518 */
519 public void validationSuccess() {
520 validationHelper.validationSuccess();
521 }
522
523
524 /***
525 * TODO: document the method
526 *
527 * @return TODO: Describe the Return Value
528 */
529 public JToolTip createToolTip() {
530 return validationHelper.createToolTip(super.createToolTip());
531 }
532
533 }
This page was automatically generated by Maven